var editor;
var CodeMirror_Helper = function(id, obj_cm) {
	obj_cm = CodeMirror.fromTextArea(document.getElementById(id), {
		lineNumbers: true,
		styleActiveLine: true,
		matchBrackets: true,
		theme: "default",
		onKeyEvent: function(i, e) {
			// Hook into ctrl-space
			if (e.keyCode == 32 && (e.ctrlKey || e.metaKey) && !e.altKey) {
				e.stop();
				return startComplete();
			}
		},
		extraKeys: {
			"F11": function(cm) {
				cm.setOption("fullScreen", !cm.getOption("fullScreen"));
			},
			"Esc": function(cm) {
				if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
			}
		}
	});
	editor = obj_cm;
	return obj_cm;
};


// Minimal event-handling wrapper.
function stopEvent() {
	if (this.preventDefault) {
		this.preventDefault();
		this.stopPropagation();
	} else {
		this.returnValue = false;
		this.cancelBubble = true;
	}
}

function addStop(event) {
	if (!event.stop)
		event.stop = stopEvent;
	return event;
}

function connect(node, type, handler) {
	function wrapHandler(event) {
		handler(addStop(event || window.event));
	}
	if (typeof node.addEventListener == "function")
		node.addEventListener(type, wrapHandler, false);
	else
		node.attachEvent("on" + type, wrapHandler);
}

function forEach(arr, f) {
	for ( var i = 0, e = arr.length; i < e; ++i)
		f(arr[i]);
}

function startComplete() {
	// We want a single cursor position.
	if (editor.somethingSelected())
		return;
	// Find the token at the cursor
	var cur = editor.getCursor(false), token = editor.getTokenAt(cur), tprop = token;
	// If it's not a 'word-style' token, ignore the token.
	if (!/^[\w$_]*$/.test(token.string)) {
		token = tprop = {
			start : cur.ch,
			end : cur.ch,
			string : "",
			state : token.state,
			className : token.string == "." ? "js-property" : null
		};
	}
	// If it is a property, find out what it is a property of.
	while (tprop.className == "js-property") {
		tprop = editor.getTokenAt({
			line : cur.line,
			ch : tprop.start
		});
		if (tprop.string != ".")
			return;
		tprop = editor.getTokenAt({
			line : cur.line,
			ch : tprop.start
		});
		if (!context)
			var context = [];
		context.push(tprop);
	}
	var completions = getCompletions(token, context);
	if (!completions.length)
		return;
	function insert(str) {
		editor.replaceRange(str, {
			line : cur.line,
			ch : token.start
		}, {
			line : cur.line,
			ch : token.end
		});
	}
	// When there is only one completion, use it directly.
	if (completions.length == 1) {
		insert(completions[0]);
		return true;
	}

	// Build the select widget
	var complete = document.createElement("div");
	complete.className = "completions";
	var sel = complete.appendChild(document.createElement("select"));
	sel.multiple = true;
	for ( var i = 0; i < completions.length; ++i) {
		var opt = sel.appendChild(document.createElement("option"));
		opt.appendChild(document.createTextNode(completions[i]));
	}
	sel.firstChild.selected = true;
	sel.size = Math.min(10, completions.length);
	var pos = editor.cursorCoords();
	complete.style.left = pos.x + "px";
	complete.style.top = pos.yBot + "px";
	document.body.appendChild(complete);
	// Hack to hide the scrollbar.
	if (completions.length <= 10)
		complete.style.width = (sel.clientWidth - 1) + "px";

	var done = false;
	function close() {
		if (done)
			return;
		done = true;
		complete.parentNode.removeChild(complete);
	}
	function pick() {
		insert(sel.options[sel.selectedIndex].value);
		close();
		setTimeout(function() {
			editor.focus();
		}, 50);
	}
	connect(sel, "blur", close);
	connect(sel, "keydown", function(event) {
		var code = event.keyCode;
		// Enter and space
		if (code == 13 || code == 32) {
			event.stop();
			pick();
		}
		// Escape
		else if (code == 27) {
			event.stop();
			close();
			editor.focus();
		} else if (code != 38 && code != 40) {
			close();
			editor.focus();
			setTimeout(startComplete, 50);
		}
	});
	connect(sel, "dblclick", pick);

	sel.focus();
	// Opera sometimes ignores focusing a freshly created node
	if (window.opera)
		setTimeout(function() {
			if (!done)
				sel.focus();
		}, 100);
	return true;
}

var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight "
		+ "toUpperCase toLowerCase split concat match replace search")
		.split(" ");
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf "
		+ "lastIndexOf every some filter forEach map reduce reduceRight ")
		.split(" ");
var funcProps = "prototype apply call bind".split(" ");
var keywords = ("break case catch continue debugger default delete do else false finally for function "
		+ "if in instanceof new null return switch throw true try typeof var void while with")
		.split(" ");

function getCompletions(token, context) {
	var found = [], start = token.string;
	function maybeAdd(str) {
		if (str.indexOf(start) == 0)
			found.push(str);
	}
	function gatherCompletions(obj) {
		if (typeof obj == "string")
			forEach(stringProps, maybeAdd);
		else if (obj instanceof Array)
			forEach(arrayProps, maybeAdd);
		else if (obj instanceof Function)
			forEach(funcProps, maybeAdd);
		for ( var name in obj)
			maybeAdd(name);
	}

	if (context) {
		// If this is a property, see if it belongs to some object we can
		// find in the current environment.
		var obj = context.pop(), base;
		if (obj.className == "js-variable")
			base = window[obj.string];
		else if (obj.className == "js-string")
			base = "";
		else if (obj.className == "js-atom")
			base = 1;
		while (base != null && context.length)
			base = base[context.pop().string];
		if (base != null)
			gatherCompletions(base);
	} else {
		// If not, just look in the window object and any local scope
		// (reading into JS mode internals to get at the local variables)
		for ( var v = token.state.localVars; v; v = v.next)
			maybeAdd(v.name);
		gatherCompletions(window);
		forEach(keywords, maybeAdd);
	}
	return found;
}

// 切换样式
$("#codemirror_theme").change(function() {
	editor.setOption("theme", $(this).val());
});


// 搜索替换
/*
 * var lastPos = null, lastQuery = null, marked = [];
 * 
 * function unmark() { for (var i = 0; i < marked.length; ++i) marked[i]();
 * marked.length = 0; }
 * 
 * function search() { unmark(); var text =
 * document.getElementById("query").value; if (!text) return; for (var cursor =
 * editor.getSearchCursor(text); cursor.findNext();)
 * marked.push(editor.markText(cursor.from(), cursor.to(), "searched"));
 * 
 * if (lastQuery != text) lastPos = null; var cursor =
 * editor.getSearchCursor(text, lastPos || editor.getCursor()); if
 * (!cursor.findNext()) { cursor = editor.getSearchCursor(text); if
 * (!cursor.findNext()) return; } editor.setSelection(cursor.from(),
 * cursor.to()); lastQuery = text; lastPos = cursor.to(); }
 * 
 * function replace() { unmark(); var text =
 * document.getElementById("query").value, replace =
 * document.getElementById("replace").value; if (!text) return; for (var cursor =
 * editor.getSearchCursor(text); cursor.findNext();)
 * editor.replaceRange(replace, cursor.from(), cursor.to()); }
 */

// })();


